//**************************************************************************/
// Copyright (c) 1998-2007 Autodesk, Inc.
// All rights reserved.
// 
// These coded instructions, statements, and computer programs contain
// unpublished proprietary information written by Autodesk, Inc., and are
// protected by Federal copyright law. They may not be disclosed to third
// parties or copied or duplicated in any form, in whole or in part, without
// the prior written consent of Autodesk, Inc.
//**************************************************************************/
// DESCRIPTION: Appwizard generated plugin
// AUTHOR: 
//***************************************************************************/

#include "Project1.h"

#include "IGame.h"
#include "IGameObject.h"
#include "IGameProperty.h"
#include "IGameControl.h"
#include "IGameModifier.h"
#include "IConversionManager.h"
#include "IGameError.h"
#include "IGameFX.h"
#pragma comment(lib, "igame.lib")

#include <AssetManagement/AssetUser.h>
#include <IFileResolutionManager.h>
#pragma comment(lib, "assetmanagement.lib")

#include <sstream>
#include <set>
#include <map>
#include <stdio.h>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
#include <fstream>
#include "XmlWriterClass.h"
#include <decomp.h>

#define MAX_UV_SETS 4
#define Project1_CLASS_ID	Class_ID(0x42a4c6b8, 0x351e9095)



class Project1 : public SceneExport {
	public:
		
		static HWND hParams;		
		
		int				ExtCount();					// Number of extensions supported
		const TCHAR *	Ext(int n);					// Extension #n (i.e. "3DS")
		const TCHAR *	LongDesc();					// Long ASCII description (i.e. "Autodesk 3D Studio File")
		const TCHAR *	ShortDesc();				// Short ASCII description (i.e. "3D Studio")
		const TCHAR *	AuthorName();				// ASCII Author name
		const TCHAR *	CopyrightMessage();			// ASCII Copyright message
		const TCHAR *	OtherMessage1();			// Other message #1
		const TCHAR *	OtherMessage2();			// Other message #2
		unsigned int	Version();					// Version number * 100 (i.e. v3.01 = 301)
		void			ShowAbout(HWND hWnd);		// Show DLL's "About..." box
		//GMatrix YtoZ;
		//GMatrix ZtoY;
		//void			AdjustMatrix(GMatrix &m);
		//bool            CheckBad(float &f);
		//void AdjustPoint(Point3 &p);
		BOOL SupportsOptions(int ext, DWORD options);
		int				DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0);

		//Constructor/Destructor
		Project1();
		~Project1();		

};



class Project1ClassDesc : public ClassDesc2 
{
public:
	virtual int IsPublic() 							{ return TRUE; }
	virtual void* Create(BOOL /*loading = FALSE*/) 		{ return new Project1(); }
	virtual const TCHAR *	ClassName() 			{ return GetString(IDS_CLASS_NAME); }
	virtual SClass_ID SuperClassID() 				{ return SCENE_EXPORT_CLASS_ID; }
	virtual Class_ID ClassID() 						{ return Project1_CLASS_ID; }
	virtual const TCHAR* Category() 				{ return GetString(IDS_CATEGORY); }

	virtual const TCHAR* InternalName() 			{ return _T("Project1"); }	// returns fixed parsable name (scripter-visible name)
	virtual HINSTANCE HInstance() 					{ return hInstance; }					// returns owning module handle
	

};

GMatrix YtoZ;
GMatrix ZtoY;


ClassDesc2* GetProject1Desc() { 
	static Project1ClassDesc Project1Desc;
	return &Project1Desc; 
}


struct FatVert {
  int           origVert;
  Point3        pos;
  Point3        normal;
  Point3        tangent;
  Point3        binormal;
  Point2        uv[MAX_UV_SETS];
  Point4        color;
};



INT_PTR CALLBACK Project1OptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {
	static Project1 *imp = NULL;

	switch(message) {
		case WM_INITDIALOG:
			imp = (Project1 *)lParam;
			CenterWindow(hWnd,GetParent(hWnd));
			return TRUE;

		case WM_CLOSE:
			EndDialog(hWnd, 0);
			return 1;
	}
	return 0;
}

XmlWriterClass* writer;
std::vector<IGameNode*> models;
std::vector<IGameNode*> helper;
std::vector<IGameNode*> light;
std::vector<IGameNode*> camera;

TCHAR* mapSlotNames[] = {
        _T("Ambient"),
        _T("Diffuse"),
        _T("Specular"),
        _T("Glossiness"),
        _T("SpecularLevel"),
        _T("SelfIllumination"),
        _T("Opacity"),
        _T("Filter"),
        _T("Bump"),
        _T("Reflection"),
        _T("Refraction"),
        _T("Displacement"),
        _T("Unknown") };


void MergeDoubleSlashes(std::string &str)
{
    std::string::size_type off;
    //  first, turn forward slashes to backward
    while ((off = str.find("/")) != std::string::npos) {
        str.replace(off, 2, "\\");
    }
    //  second, turn double backward slashes to single
    while ((off = str.find("\\\\")) != std::string::npos) {
        str.replace(off, 2, "\\");
    }
}

static string convert_name(TCHAR const * name)
{
  if (name == 0) {
    return string(_T("unnamed"));
  }
  string ret(name);
  if ((name[0] >= '0') && (name[0] <= '9')) {
    ret = std::string("_") + ret;
  }
  for (size_t i = 0; i < ret.size(); ++i) {
    TCHAR ch = ret[i];
    if (((ch >= '0') && (ch <= '9'))
        || ((ch >= 'A') && (ch <= 'Z'))
        || ((ch >= 'a') && (ch <= 'z'))) {
      continue;
    }
    ret[i] = '_';
  }
  return ret;
}


std::string GetFullPath(char const *path)
{
  std::string texPath(IFileResolutionManager::GetInstance()->GetFullFilePath(
      path, MaxSDK::AssetManagement::kBitmapAsset));
  if (texPath.empty())
    texPath = path;
  MergeDoubleSlashes(texPath);
  return texPath;
}

std::string MakeRelativePath(std::string const &input, std::string const &prefix)
{
  char const *outputName = strrchr(input.c_str(), '\\');
  if (!outputName) outputName = strrchr(input.c_str(), '/');
  if (!outputName) outputName = input.c_str(); else ++outputName;
  if (*outputName) {
    return prefix + outputName;
  }
  return input;
}

bool tangentBad = false;
bool binormalBad = false;
std::vector<IGameNode *> bones;
bool hasSkin = false;

struct AnimationRange {
  std::string name;
  int first;
  int length;
  float timeStretch;
};



//--- Project1 -------------------------------------------------------
Project1::Project1()
{

}

Project1::~Project1() 
{

}

int Project1::ExtCount()
{
	#pragma message(TODO("Returns the number of file name extensions supported by the plug-in."))
	return 1;
}

const TCHAR *Project1::Ext(int n)
{		
	#pragma message(TODO("Return the 'i-th' file name extension (i.e. \"3DS\")."))
	return _T("xml");
}

const TCHAR *Project1::LongDesc()
{	
	return _T("Test Scene Exporter");
}
	
const TCHAR *Project1::ShortDesc() 
{		
	return _T("Test Scene Exporter");
}

const TCHAR *Project1::AuthorName()
{	
	return _T("Thiago Dias Pastor");
}

const TCHAR *Project1::CopyrightMessage() 
{		
	return _T("Free for all");
}

const TCHAR *Project1::OtherMessage1() 
{	
	
	return _T("");
}

const TCHAR *Project1::OtherMessage2() 
{		
	
	return _T("");
}

unsigned int Project1::Version()
{				
	#pragma message(TODO("Return Version number * 100 (i.e. v3.01 = 301)"))
	return 100;
}

void Project1::ShowAbout(HWND hWnd)
{			
	// Optional
}
class Remap {
  public:
    Remap() {
    }
    std::vector<std::vector<int> > oldToNew_;
    void SetOldCount(int count) {
        oldToNew_.clear();
        oldToNew_.resize(count);
    }
    void MapOldToNew(int old, int nu) {
        assert(old >= 0 && old < (int)oldToNew_.size());
        oldToNew_[old].push_back(nu);
    }
    template<typename T>
    void ForeachOld(int old, T & func) {
        assert(old >= 0 && old < oldToNew_.size());
        std::vector<int>::iterator ptr = oldToNew_[old].begin(), end = oldToNew_[old].end();
        while (ptr != end) {
            func(*ptr);
            ++ptr;
        }
    }
    int GetCount(int old) {
           if (old < 0 || old >= (int)oldToNew_.size()) return -1;
        return (int)oldToNew_[old].size();
    }
    int GetNew(int old, int i) {
        if (old < 0 || old >= (int)oldToNew_.size()) return -1;
        if (i < 0 || i >= (int)oldToNew_[old].size()) return -1;
        return oldToNew_[old][i];
    }
};


BOOL Project1::SupportsOptions(int ext, DWORD options)
{
	#pragma message(TODO("Decide which options to support.  Simply return true for each option supported by each Extension the exporter supports."))
	return TRUE;
}

//  This function fixes up degenerate tangent bases that may come 
//  from stretched UV mapping or degenerate faces.
static void FixTangent(FatVert &fv, bool &oFixed)
{
  if (fv.tangent.LengthSquared() < 1e-20f)
  {
    oFixed = true;
    if (fabsf(fv.normal.x) < fabsf(fv.normal.y) && fabsf(fv.normal.x) < fabsf(fv.normal.z))
    {
      //  if X is the smallest, go with it
      fv.tangent = Point3(1, 0, 0);
    }
    else
    {
      //  else go with Y -- we know that X is non-trivial, thus Y is not linearly dependent
      fv.tangent = Point3(0, 1, 0);
    }
  }
}

static void FixBinormal(FatVert &fv, bool &oFixed)
{
  if (fv.binormal.LengthSquared() < 1e-20f)
  {
    oFixed = true;
    fv.binormal = CrossProd(fv.normal, fv.tangent);
  }
}


//  Transform a point (such as a vertex)
static Point3 TransformPoint(GMatrix const & gm, Point3 const & p)
{
  float out[3];
  float const * v = &p.x;
  GRow const * m = gm.GetAddr();
  for (int i = 0; i < 3; ++i) {
    float o = m[3][i];    //  different from TransformVector here
    for (int j = 0; j < 3; ++j) {
      o += m[j][i] * v[j];
    }
    out[i] = o;
  }
  return (Point3 &)out;
}

//  Transform a vector (such as a normal)
static Point3 TransformVector(GMatrix const & gm, Point3 const & p)
{
  float out[3];
  float const * v = &p.x;
  GRow const * m = gm.GetAddr();
  for (int i = 0; i < 3; ++i) {
    float o = 0;        //  different from TransformPoint here
    for (int j = 0; j < 3; ++j) {
      o += m[j][i] * v[j];
    }
    out[i] = o;
  }
  return (Point3 &)out;
}


static bool CheckBad(float &f) {
  //  these tests weed out NaNs and the like
  if (f > 1.0 && f < 0.9) return true;
  if (!(f > 0.9) && !(f < 1.0)) return true;
  //  test for too large numbers (and inf)
  if (f > 1e30) return true;
  if (f < -1e30) return true;
  //  nuke denormals
  if (::fabsf(f) < 1e-30) f = 0;
  //  OK, the number is acceptable
  return false;
}

//  Note: don't apply scale. This function is also 
//  used for normals. Could potentially split it into 2.
static void AdjustPoint(Point3 &p)
{
  if (CheckBad(p.x) || CheckBad(p.y) || CheckBad(p.z)) {
    p.x = 1; p.y = 0; p.z = 0;
  }
  
  p.y = -p.y;
  
  
  float y = p.y;
  p.y = p.z;
  p.z = -y;
  
}


static void AdjustMatrix(GMatrix &m)
{
  
    //  I mirror Y in Max space (Y == out)
    m.SetColumn(1, m.GetColumn(1) * -1);
    m.SetRow(1, m.GetRow(1) * -1);
  
  
    m = YtoZ * m * ZtoY;
  
  Point3 x = m.Translation() ;
  m.SetRow(3, Point4(x, 1));
}

static DWORD VertexAdd(std::vector<FatVert> & vec, std::vector<std::pair<float, DWORD> > & acc, FatVert const & fv)
{
  size_t n = vec.size();
  size_t i = 0;
  std::pair<float, DWORD> p(fv.pos.x, 0);
  std::pair<float, DWORD> * base = 0;
  //  If the acceleration list has items in it, then do a binary search for 
  //  a matching X component, to make finding vertices to merge easier.
  //  TODO: This really should be a hash table.
  if (acc.size() > 0) {
    std::pair<float, DWORD> * low = &acc[0], * high = low+acc.size();
    base = low;
    for (;;) {
      std::pair<float, DWORD> * mid = low + (high-low)/2;
      if (mid == low) {
        i = mid-&acc[0];
        break;
      }
      if (mid->first < p.first) {
        low = mid;
      }
      else {
        high = mid;
      }
    }
  }
  for (; i < n; ++i) {
    if (vec[base[i].second].pos.x > fv.pos.x) {
      //  no need searching more
      i = base[i].second;
      break;
    }
    if (!memcmp(&vec[base[i].second], &fv, sizeof(fv))) {
      //  Put a breakpoint here to ensure that I get some vertex sharing.
      //  If I don't at all, then the binary search above is busted.
      return base[i].second;
    }
  }
  p.second = (DWORD)vec.size();

  size_t nv = vec.size();
  for (size_t i = 0; i < nv; ++i) {
    assert( memcmp(&fv, &vec[i], sizeof(FatVert)) );
  }

  //  update the sorted list that's used to accelerate finding shared vertices
  acc.push_back(p);
  std::inplace_merge(&acc[0], &acc[acc.size()-1], &acc[0]+acc.size());
  vec.push_back(fv);
  return p.second;
}

void GetAnimationRanges(std::string const & b, std::vector<AnimationRange> & cont)
{
  AnimationRange ar;
  char const * x = b.c_str();
  char * e = (char *)x;
  long l = strtol(x, &e, 10);
  if (e != x) {
    x = e+1;
    for (long q = 0; q < l; ++q) {
      //  what a horrible parsing loop!
      e = (char *)strchr(x, ',');
      if (!e) goto done;
      std::string wt(x, e-x);
      ar.name = wt;
      x = e+1;
      e = (char *)strchr(x, ',');
      if (!e) goto done;
      wt = std::string(x, e);
      ar.first = strtol(wt.c_str(), (char **)&x, 10);
      x = e+1;
      e = (char *)strchr(x, ',');
      if (!e) goto done;
      wt = std::string(x, e);
      ar.length = strtol(wt.c_str(), (char **)&x, 10);
      x = e+1;
      e = (char *)strchr(x, ';');
      if (!e) goto done;
      wt = std::string(x, e);
      ar.timeStretch = (float)strtod(wt.c_str(), (char **)&x);
      x = e+1;
      cont.push_back(ar);
    }
  done:
    ;
  }
}

static bool almostEqual(Point3 const & a, Point3 const & b)
{
  return fabsf(a.x-b.x) < 1e-4 && fabsf(a.y-b.y) < 1e-4 && fabsf(a.z-b.z) < 1e-4;
}

static bool almostEqual(Quat const & a, Quat const & b)
{
  return fabsf(a.x-b.x) < 1e-4 && fabsf(a.y-b.y) < 1e-4 && fabsf(a.z-b.z) < 1e-4 && fabsf(a.w-b.w) < 1e-4;
}

static float dot(Quat const & q, Quat const & p)
{
  return q.x*p.x + q.y*p.y + q.z*p.z + q.w*p.w;
}

void PutAnimationRanges(std::vector<AnimationRange> const & cont, std::string & b)
{
  std::stringstream oot;
  oot << cont.size() << ";";
  for (std::vector<AnimationRange>::const_iterator ptr = cont.begin(), end = cont.end();
      ptr != end; ++ptr)
  {
    oot << (*ptr).name << "," << (*ptr).first << "," << (*ptr).length << "," << (*ptr).timeStretch << ";";
  }
  b = oot.str();
}



void ExportAnimations()
{  

	
  writer->BeginWriteAnimationInfos("testeAnimation");
  std::vector<AnimationRange> vec;  

  AnimationRange ar;
  ar.first = 1;
  ar.length = 50;
  ar.name = "testeAnimation";
  ar.timeStretch = 1;

  if (vec.size() == 0) {
    //  If there are no animations configured, use the 
    //  settings from the settings fields.
    vec.push_back(ar);
  }

  for (std::vector<AnimationRange>::iterator arp = vec.begin(), are = vec.end();
      arp != are; ++arp) {
    ar = *arp;
    std::vector<IGameNode *>::iterator ptr, end;
    
    DWORD dwd = 4800;   //  3dsMax tick rate is 4800 ticks per second
    
    dwd /= GetFrameRate();
    DWORD outTime = (DWORD)(dwd * ar.timeStretch);
    if (outTime < 0) outTime = 0;
    int temp = 0;
    
    for (ptr = bones.begin(), end = bones.end(); ptr != end; ++ptr) {
      IGameNode * node = *ptr;
      std::vector<std::pair<int, Point3>> posAnim;
      std::vector<std::pair<int, Point3>> scaleAnim;
      std::vector<std::pair<int, Quat>> quatAnim;
      Point3 op(-101010, 101010, -101010);
	  Point3 os(0,0,0);
      Quat oq(1.0f, 1.0f, 1.0f, 1.0f);
      //  Remember how many frames were dropped, so that the last keyframe 
      //  before a change can be put back in.
      int compPos = 0;
      int compRot = 0;
      int compScale = 0;
      for (int time = 0; time < ar.length; ++time) {
		bool save = false;
        GMatrix ip;
        IGameNode * np = node->GetNodeParent();
        if (np) {
          ip = np->GetWorldTM(TimeValue((time + ar.first)*dwd));
          AdjustMatrix(ip);
          ip = ip.Inverse();
        }
        else {
          ip.SetIdentity();
        }
        GMatrix tm = node->GetWorldTM(TimeValue((time + ar.first)*dwd));
        AdjustMatrix(tm);
        tm = tm * ip;
        //  decompose
        Matrix3 m3 = tm.ExtractMatrix3();
        Point3 p;
        Quat q;
        Point3 s;
        DecomposeMatrix(m3, p, q, s);
        //  add components
        if (!almostEqual(p, op) || time == 0) {
			save = true;
          /*if (compPos > 0) {
            posAnim.push_back(std::pair<DWORD, Point3>(
              (time-1)*outTime, op) );
          }
          posAnim.push_back( std::pair<DWORD, Point3>(
              time*outTime, p) );*/

          op = p;
          compPos = 0;
        }
        else {
          ++compPos;
        }
        if (!almostEqual(s, os) || time == 0) {
			save = true;
          /*if (compScale > 0) {
            scaleAnim.push_back(std::pair<DWORD, Point3>(
                (time-1)*outTime, os));
          }
          scaleAnim.push_back(std::pair<DWORD,Point3>(
              time*outTime, s));*/
          os = s;
          compScale = 0;
        }
        else {
          ++compScale;
        }
        if (dot(q, oq) < 0) {
          //  take the short way around
          q = Quat(-q.x, -q.y, -q.z, -q.w);
        }
        if (!almostEqual(q, oq) || time == 0) {          
			save = true;
          /*if (compRot > 0) {
            quatAnim.push_back(std::pair<DWORD, Quat>(
                (time-1)*outTime, Quat(oq.w, oq.x, oq.y, oq.z)));
          }
          quatAnim.push_back(std::pair<DWORD,Quat>(
              time*outTime, Quat(q.w, q.x, q.y, q.z)));*/
          oq = q;
          compRot = 0;
        }
        else {
          ++compRot;
        }


		if(save)
		{
			writer->WriteAnimationClip(node->GetName(),time*outTime,p,Point4(q.x,q.y,q.z,q.w),s);
		}


      }
      //anim = 0;
      std::vector<char> foo;
      //append(foo, (DWORD)0); // rotation animation
      //append(foo, (DWORD)quatAnim.size());
      //append(foo, quatAnim);	  

      int temp = 0;
      //CHECK( anim->AddDataObject(TID_D3DRMAnimationKey, "rot", 0, foo.size(), &foo[0], &temp) );
      temp = 0;
      foo.clear();
      //append(foo, (DWORD)1); // scale animation
      //append(foo, (DWORD)scaleAnim.size());
      //append(foo, scaleAnim);
      //CHECK( anim->AddDataObject(TID_D3DRMAnimationKey, "scale", 0, foo.size(), &foo[0], &temp) );
      temp = 0;
      foo.clear();
      //append(foo, (DWORD)2); // position animation
      //append(foo, (DWORD)posAnim.size());
      //append(foo, posAnim);
      //CHECK( anim->AddDataObject(TID_D3DRMAnimationKey, "pos", 0, foo.size(), &foo[0], &temp) );
    }
  }

	writer->EndWriteAnimationInfos();
}



void WriteGeometry(IGameScene * ig, IGameNode * node, IGameObject * obj,TimeValue coreFrame_)
{
  hasSkin = false;
  int exportUv  = MAX_UV_SETS;

	//  retrieve the appropriate geometry
  std::vector<char> mesh;
  IGameMesh * gm = static_cast<IGameMesh *>(obj);  
  IGameSkin * gs = obj->IsObjectSkinned() ? obj->GetIGameSkin() : 0;
  if (gs ) {
    gm = gs->GetInitialPose();
	hasSkin = true;
  }
  gm->SetCreateOptimizedNormalList();  
  
  if (!gm->InitializeData()) {}

  //  When I ask for vertices, the object offset is already 
  //  applied, so always use the node TM.
  GMatrix nwm = node->GetWorldTM(coreFrame_);
  //  inverse transpose of node-to-obj
  if (gs ) {
    gs->GetInitSkinTM(nwm);
    //  Perhaps GetSkinPose() already includes InitSkinTM()?
    nwm.SetIdentity();
  }
  AdjustMatrix(nwm);
  GMatrix worldToNode = nwm.Inverse();
  GMatrix normalMat = worldToNode;
  

  DWORD numFaces = gm->GetNumberOfFaces();
  DWORD numFacesUsed = 0;
  std::vector<DWORD> indices;
  indices.resize(numFaces * 3);
  FatVert fv;
  std::vector<FatVert> fatVerts;
  std::vector<std::pair<float, DWORD> > accel;

  //  don't export texture coordinates that aren't available
  Tab<int> mapNums = gm->GetActiveMapChannelNum();
  //  look for the requested map numbers
  std::vector<int> useMapNums;
  for (int i = 0; i < mapNums.Count(); ++i)
    if (mapNums[i] >= 1)
      useMapNums.push_back(mapNums[i]);

  if (useMapNums.size() < (size_t)exportUv) {    
    exportUv = (int)useMapNums.size();
  }

  if (exportUv == 0) {    
  }

  if (exportUv > MAX_UV_SETS) {    
    exportUv = MAX_UV_SETS;
  }

  //  extract all unique vertices on a per-face basis
  std::vector<DWORD> materialIndices;
  //  some materials may not be included
  std::vector<IGameMaterial *> materials;
  int maxIx = -1;
  std::vector<bool> includeFace;
  int nColor = gm->GetNumberOfColorVerts();
  int nAlpha = gm->GetNumberOfAlphaVerts();
  for (int i = 0; i < (int)numFaces; ++i) {
    FaceEx * fex = gm->GetFace(i);
    IGameMaterial * imat = gm->GetMaterialFromFace(fex);
    size_t nMat = materials.size();

	/////////DARK MAGIC pra ver se o vetor ja contem o material, i love cplusplus
	if(imat!= NULL && std::find(materials.begin(),materials.end(),imat) == materials.end())
	{
		materials.push_back(imat);
	}
	

    //bool inc;
    //for (size_t m = 0; m < nMat; ++m) {
    //  if (materials[m].first == imat) {
    //    includeFace.push_back(materials[m].second);
    //    if (!materials[m].second) {
    //      goto dont_include;
    //    }
    //    materialIndices.push_back((DWORD)m);
    //    goto got_mat;
    //  }
    //}
        
    //materials.push_back(std::pair<IGameMaterial *, bool>(imat, inc));
    //includeFace.push_back(inc);
    //if (!inc) {
    //  goto dont_include;
    //}
    //materialIndices.push_back((DWORD)materials.size()-1);
//got_mat:
    for (int j = 0; j < 3; ++j) {
      memset(&fv, 0, sizeof(fv));
      fv.origVert = fex->vert[j];
      fv.pos = gm->GetVertex(fex->vert[j], false) ;
      AdjustPoint(fv.pos);
      fv.pos = fv.pos * worldToNode;
      //  additional fields for each vertex based on options
      fv.normal = gm->GetNormal(fex->norm[j], false);
      AdjustPoint(fv.normal);
      fv.normal = TransformVector(normalMat, fv.normal);
      

	  ///Getting Tangents an Binormal
      /*if (exportTangent) {*/
        int tbi = gm->GetFaceVertexTangentBinormal(i, j, 1);
        fv.tangent = gm->GetTangent(tbi, 1);
        AdjustPoint(fv.tangent);
        fv.tangent = TransformVector(normalMat, fv.tangent);
        FixTangent(fv, tangentBad);
        fv.binormal = gm->GetBinormal(tbi, 1);
        AdjustPoint(fv.binormal);
        fv.binormal = TransformVector(normalMat, fv.binormal);
        FixBinormal(fv, binormalBad);
        /*if (flipTangent) {
          fv.tangent *= -1;
        }*/
	   //}
      

      for (int q = 0; q < exportUv; ++q) {
        DWORD index[3] = {0, 0, 0};
        //  maps are indexed by their Max usage, not by index.
        //  Texture coordinates start at 1, and we've verified 
        //  that the maps at index 0..exportUv-1 are available.
        gm->GetMapFaceIndex(useMapNums[q], i, index);
        fv.uv[q] = (Point2&)gm->GetMapVertex(q+1, index[j]);
        //  The default is a need to flip the texture, at least 
        //  when it's a JPG or similar.
       /* if (!flipV_)*/ {
          fv.uv[q].y = 1.0f - fv.uv[q].y;
        }
      }      
      indices[numFacesUsed * 3 + j] = VertexAdd(fatVerts, accel, fv);
    }
    ++numFacesUsed;
//dont_include:
  //  ;
  }
  indices.resize(numFacesUsed*3);
  //  flip faces if swapping to right-handed space
  //if (rightHanded != flipWinding) {
    /*for (int i = 0; i < (int)numFacesUsed; ++i) {
      std::swap(indices[i*3+1], indices[i*3+2]);
    }*/
  //}


  //  generate the initial Mesh geometry data
  DWORD numVertices = (DWORD)fatVerts.size();
  if (numVertices == 0) {
    return; // WE GOT A SITUATION !!!! HOUHOUHOU
  }
  std::vector<Point3> verts;
  verts.resize(numVertices);
  for (int i = 0; i < (int)numVertices; ++i) 
  {
    verts[i] = fatVerts[i].pos;	
  }

  /*append(mesh, verts);
  append(mesh, numFacesUsed);*/  
  writer->BeginWriteIndices();
  DWORD tri[3];  
  DWORD curUsedIx = 0;
  for (int i = 0; i < (int)numFaces; ++i) {
//    if (includeFace[i]) {
      for (int j = 0; j < 3; j++) {
        tri[j] = indices[curUsedIx*3+j];
		writer->WriteIndice(tri[j]);
      }
      //append(mesh, tri, 4);
      ++curUsedIx;
  //  }
  }
  writer->EndWriteIndices();
  assert(curUsedIx == numFacesUsed);
  
  //  Calculate mapping from old vertex index to new vertex indices
  Remap remap;
  if (fatVerts.size() > 0) {
    FatVert * fvp = &fatVerts[0], * base = fvp, * end = fvp + fatVerts.size();
    int mo = -1;
    while (fvp < end) {
      if (mo < fvp->origVert) {
        mo = fvp->origVert;
      }
      ++fvp;
    }
    remap.SetOldCount(mo + 1);
    fvp = base;
    while (fvp < end) {
      remap.MapOldToNew((int)fvp->origVert, (int)(fvp-base));
      ++fvp;
    }
  }

    //  write normals      
	std::vector<Point3> norms;
    DWORD numNormals = (DWORD)fatVerts.size();
    //append(normals, numNormals);
    norms.resize(numNormals);
    for (int i = 0; i < (int)numNormals; ++i) {
      norms[i] = fatVerts[i].normal;
    }

	//  write tangents
	std::vector<Point3> tangents;
    DWORD numTangs = (DWORD)fatVerts.size();    
    tangents.resize(numTangs);
    for (int i = 0; i < (int)numTangs; ++i) {
      tangents[i] = fatVerts[i].tangent;
    }

	//  write binormals
	std::vector<Point3> binormals;
    DWORD numB = (DWORD)fatVerts.size();    
    binormals.resize(numB);
    for (int i = 0; i < (int)numB; ++i) {
		binormals[i] = fatVerts[i].binormal;
    }

    
    curUsedIx = 0;
    for (int i = 0; i < (int)numFaces; ++i) {
      //if (includeFace[i]) {
        for (int j = 0; j < 3; ++j) {
          tri[j+1] = indices[curUsedIx*3+j];
        }
        //append(normals, tri, 4);
        ++curUsedIx;
      //}
    }
    assert(curUsedIx == numFacesUsed);
  

  DWORD numTexCoords = numVertices;
  std::vector<Point2> tex;
  tex.resize(numTexCoords);
  //  write first texcoords
  if (exportUv > 0) {
    std::vector<char> texcoords;    
    //append(texcoords, numTexCoords);    
    for (int i = 0; i < (int)numTexCoords; ++i) {
      tex[i] = fatVerts[i].uv[0];
    }
    //append(texcoords, tex);    
  }

  
   writer->BeginWriteVertices();
	for(int i = 0; i < numVertices;i++)
	{
		writer->WriteVertice(verts[i],norms[i],tex[i],tangents[i],binormals[i]);
	}
	writer->EndWriteVertices();

  // write tangent basis and extra texcoords
  if (exportUv > 1) {

    int i = 0;
    WORD offset = 0;
    for (int uvSet = 1; uvSet < exportUv; ++uvSet) {
      //D3DVERTEXELEMENT9 ve = { 0, offset, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, uvSet };
      //elems[i] = ve;
      ++i;
      offset += 8;
    }
    /*if (exportTangent) {
      D3DVERTEXELEMENT9 ve1 = { 0, offset, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0 };
      offset += 12;
      D3DVERTEXELEMENT9 ve2 = { 0, offset, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 };
      offset += 12;
      elems[i] = ve1;
      ++i;
      elems[i] = ve2;
      ++i;
    }*/
    std::vector<char> data;
//    append(data, (DWORD)i);
    for (int j = 0; j < i; ++j) {
     /* append(data, (DWORD)elems[j].Type);
      append(data, (DWORD)elems[j].Method);
      append(data, (DWORD)elems[j].Usage);
      append(data, (DWORD)elems[j].UsageIndex);*/
    }
    DWORD dSize = offset/4 * numVertices;
//    append(data, dSize);
    for (DWORD d = 0; d < numVertices; ++d) {
      for (int uvSet = 1; uvSet < exportUv; ++uvSet) {
        //append(data, (Point2 const &)fatVerts[d].uv[uvSet]);
      }
      /*if (exportTangent) {
        append(data, (Point3 const &)fatVerts[d].binormal);
        append(data, (Point3 const &)fatVerts[d].tangent);
      }*/
    }    
  }

/////////////////////////////////// SKIN //////////////////////////////////////////////////////////////

   std::vector<int> unweightedVerts;

  //  write skinning data if present
  if (gs) {

	  writer->BeginWriteSkinInfo();

	  writer->BeginWriteBones();

    //  build a table from vertex to array of bones
    struct BoneWeight {
      int index;
      float weight;
      bool operator==(BoneWeight const & o) const {
        return o.weight == weight;
      }
      bool operator<(BoneWeight const & o) const {
        //  sort biggest first
        return o.weight < weight;
      }
    };
    // build the cross-mapping of verticies used by bones
    std::vector<std::vector<BoneWeight> > assignments;
    std::map<IGameNode *, int> boneIndices;
    std::map<IGameNode *, int>::iterator ptr;
    int vc = gs->GetNumOfSkinnedVerts();

    //  If a single vertex is weighted to more than 40 bones, 
    //  truncate at 40.
    BoneWeight bw[40];
    int numMax = 0;
    for (int vi = 0; vi < vc; ++vi) { ///number of skinnedVertices
      int nb = gs->GetNumberOfBones(vi);
      if (nb > 40) {
        nb = 40;
      }
      for (int i = 0; i < nb; ++i) { ///bones of this vertice
        IGameNode * node = gs->GetIGameBone(vi, i); ///vertice,bone
		IGameObject *obj =  node->GetIGameObject();				
		if(obj->GetIGameType() ==  IGameObject::IGAME_MAX_BONE)
		{
			
		}

        if (node == NULL) {
          if (i == 0) {
            unweightedVerts.push_back(vi);
          }
          continue;
        }

        ptr = boneIndices.find(node);
        if (ptr != boneIndices.end()) { 
		  ///ja tem na lista
          bw[i].index = (*ptr).second;
        }
        else { 
		  ///novo
          bw[i].index = (int)boneIndices.size();
          boneIndices[node] = bw[i].index;
          assignments.push_back(std::vector<BoneWeight>());
        }
        bw[i].weight = gs->GetWeight(vi, i);
      }
      std::sort(bw, &bw[nb]);
      if (nb < 4) {
        memset(&bw[nb], 0, (4-nb)*sizeof(BoneWeight));
        if (nb > numMax) {
          numMax = nb;
        }
      }
      else {
        numMax = 4;
      }
      //  normalize the 4 highest weighted
      float sum = bw[0].weight + bw[1].weight + bw[2].weight + bw[3].weight;
      sum = 1.0f / sum;
      bw[0].weight *= sum; bw[1].weight *= sum; bw[2].weight *= sum; bw[3].weight *= sum;
      for (int i = 0; i < 4; ++i) {
        BoneWeight ww;
        ww.index = vi; ///Vertice
        ww.weight = bw[i].weight; ///Peso
        //  don't include a vert that's not actually affected
        if (ww.weight > 0) {			            
          assignments[bw[i].index].push_back(ww);
		  ///BoneIndex -> BoneWeight(Vertice e Peso)
        }

      }
    }////end of skinnedvertices

    //  calculate number of bones that affect the mesh
    int numAffect = 0;
    for (size_t as = 0; as < assignments.size(); ++as) {
      if (assignments[as].size() > 0) {
        ++numAffect;
      }
    }

    if (unweightedVerts.size() > 0) {
      size_t n = unweightedVerts.size();
      char const *str = "";
      if (n > 30) {
        n = 30;
        str = "...";
      }
      //std::string ss;
      //ss << "The following vertices are not weighted to any bone:\n";
      //for (size_t i = 0; i < n; ++i) {
        //ss << (i > 0 ? ", " : "") << unweightedVerts[i];
      //}
      //ss << str;
      //ss << "\n(mesh name " << SafeName(node, "mesh") << ")";      
    }
    
	

    //  write an SkinInfo for each bone that affects the mesh
    for (std::map<IGameNode *, int>::iterator ptr = boneIndices.begin(), end = boneIndices.end();
        ptr != end; ++ptr) 
	{
      IGameNode * bone = (*ptr).first;
      std::vector<char> skinData;
      std::string s(convert_name(bone->GetName()));	  
      //skinData.insert(skinData.end(), s.c_str(), s.c_str()+s.length()+1);
      int bix = (*ptr).second;
      std::vector<BoneWeight> & bwv = assignments[bix];
      if (bwv.size()) {
        std::vector<int> skinIndices;
        std::vector<float> skinWeights;
        BoneWeight * bw = &bwv[0];
        DWORD n  =(DWORD)bwv.size();
        for (DWORD t = 0; t < n; ++t) {
          int oix = bw[t].index;
          float ow = bw[t].weight;
          int oixCnt = remap.GetCount(oix);
          //  Each old vert may generate more than one new vert
          for (int i = 0; i < oixCnt; ++i) {
            int x = remap.GetNew(oix, i);
            if (x == -1) break;
            assert((unsigned int)x < numVertices && x >= 0);
            skinIndices.push_back(x);   ///VerticesId
            skinWeights.push_back(ow);  ///pesos			
          }
        }
        //append(skinData, (DWORD)skinIndices.size());
        //append(skinData, skinIndices);
        //append(skinData, skinWeights);		

		////////////WRITETOFILE/////////////////

        //  Calculate bone matrix at init time.
        //  Then calculate the matrix that takes the init vertex to the 
        //  init bone space.
        GMatrix boneInitTM;
        gs->GetInitBoneTM(bone, boneInitTM);
        AdjustMatrix(boneInitTM);
#define BONE_INIT_IS_PARENT_RELATIVE 0
#if BONE_INIT_IS_PARENT_RELATIVE
        GMatrix invParent;
        IGameNode *boneParent = bone->GetNodeParent();
        if (boneParent != NULL) {
          gs->GetInitBoneTM(boneParent, invParent);
          AdjustMatrix(invParent);
          boneInitTM = invParent * boneInitTM;
        }
#endif
#define BONE_INIT_INCLUDES_SKIN_INIT 0
#if BONE_INIT_INCLUDES_SKIN_INIT
        GMatrix invSkin;
        gs->GetInitSkinTM(invSkin);
        AdjustMatrix(invSkin);
        boneInitTM = boneInitTM * invSkin;
#endif

		AffineParts apVal;
		decomp_affine(boneInitTM.Inverse().ExtractMatrix3(), &apVal);

		Point4 quat;
		quat.x = apVal.q.x;
		quat.y = apVal.q.y;
		quat.z = apVal.q.z;
		quat.w = apVal.q.w;		

		bones.push_back(bone);

		std::vector<const char*> cnames;
		int numChilds = bone->GetChildCount();		
		for(int nc = 0 ; nc < numChilds; nc++)
		{
			cnames.push_back(bone->GetNodeChild(nc)->GetName());
		}

		IGameNode * fnode = bone->GetNodeParent();
		char* nome = "";
		if(fnode)
		{		
			nome =  fnode->GetName();
		}
		writer->WriteBone(nome, cnames , s.c_str(),skinIndices,skinWeights,apVal.t,quat,apVal.k);

        //append(skinData, boneInitTM.Inverse());
        //CComPtr<ID3DXFileSaveData> saveSkin;
        //CHECK( stored->AddDataObject(DXFILEOBJ_SkinWeights, (std::string("W-") + s).c_str(), 0, skinData.size(), &skinData[0], &saveSkin) );
      }
    }	  	

	 writer->EndWriteBones();

	 if(hasSkin)
     { 
		ExportAnimations();
	 }
	 writer->EndWriteSkinInfo();


  }

////////////////////////////////////////////

    bool ecxistOneNotNull = materials.size() > 0 ? true : false; ///isto non ecxiste hohohohooh
	
	if(ecxistOneNotNull)
		writer->BeginWriteMaps();

	for (size_t i = 0; i < materials.size(); ++i) {

		/*char *mtlName = materials[i]->GetMaterialName();
		IGameMaterial * theMat = materials[i];
        IGameProperty * matProp = theMat->GetDiffuseData();
		if (!matProp) {} ///NOT STD MATERIAL...... todo
		int type = matProp->GetType();
		assert(type == IGAME_POINT3_PROP);*/	

	  int nText = materials[i] ? materials[i]->GetNumberOfTextureMaps() : 0;
      std::map<string, string> textures;
      for (int bi = 0; bi < nText; ++bi) {
        IGameTextureMap * gtx = materials[i]->GetIGameTextureMap(bi);
        if (!gtx || !gtx->IsEntitySupported()) {
            //contains a texture map type that is not supported

        }

        int slot = gtx->GetStdMapSlot();
        if (slot < 0 || slot >= sizeof(mapSlotNames)/sizeof(mapSlotNames[0])) {
          // work around crash when there's no actual map in the material
          //Material contains a bad or missing texture map 
			continue;
		  
        }
        TCHAR const *texPathData = gtx->GetBitmapFileName();
        if (texPathData == NULL || texPathData[0] == 0) {
          // work around crash when there's no path in the map
         //contains a bitmap without a file path
             continue;
        }
		
		std::string texPath(texPathData);
        texPath = GetFullPath(texPath.c_str());
        std::string origPath(texPath.c_str());
        /*if (renameTex) {
          std::string::size_type st = texPath.rfind('.');
          if (st != texPath.npos) {
            texPath.replace(st, texPath.size()-st, ".dds");
          }
        }*/
        
        texPath = MakeRelativePath(texPath, "");        
        const char* outputName = texPath.c_str();         		

		const char* mname = mapSlotNames[slot]; 
		writer->WriteMap(mname,outputName) ;
      }	  
	}
	if(ecxistOneNotNull)
		writer->EndWriteMaps();

}


void GetNodes(IGameScene * ig, IGameNode * node)
{
	
			IGameObject *obj =  node->GetIGameObject();
			if (obj) {
			  if (obj->GetIGameType() == IGameObject::IGAME_SPLINE) { }
			  else if (obj->IsEntitySupported()) 
			  {
				switch (obj->GetIGameType()) 
				{
				case IGameObject::IGAME_MESH:
					  {			
						  models.push_back(node);
					  }
					  break;
				case IGameObject::IGAME_HELPER:
					{
						helper.push_back(node);
					}
					break;
				case IGameObject::IGAME_LIGHT:
					{
						light.push_back(node);
					}
					break;
				case IGameObject::IGAME_CAMERA:
					{
						camera.push_back(node);
					}
					break;
					  default:
					// do nothing
					break;
				}
			  }
			}				
		int num = node->GetChildCount();
		for(int i = 0 ; i < num; i++)
		{
			IGameNode * son = node->GetNodeChild(i);
			GetNodes(ig,son);
		}

}

int	Project1::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options)
{
		#pragma message(TODO("Implement the actual file Export here and"))
	
		models.clear();
		bones.clear();
		helper.clear();
		light.clear();
		camera.clear();
		tangentBad = false;
		binormalBad = false;
		writer = new XmlWriterClass(name);	
	    IGameScene * igame_ = GetIGameInterface();
		TimeValue coreFrame_;
		Interface * core_= GetCOREInterface();

		coreFrame_ = core_->GetTime();

	    IGameConversionManager * cm = GetConversionManager();
		cm->SetCoordSystem(IGameConversionManager::IGAME_MAX);
		igame_->InitialiseIGame(false);
		igame_->SetStaticFrame(coreFrame_);

		  ///Setting up some matrix
		  YtoZ.SetRow(0, Point4(1, 0, 0, 0));
		  YtoZ.SetRow(1, Point4(0, 0, 1, 0));
		  YtoZ.SetRow(2, Point4(0, -1, 0, 0));
		  YtoZ.SetRow(3, Point4(0, 0, 0, 1));
		  ZtoY = YtoZ.Inverse();
		
		  int nObjects = igame_->GetTopLevelNodeCount();
		  for (int i = 0; i < nObjects; ++i) 
		  {
			IGameNode * n = igame_->GetTopLevelNode(i);			
			GetNodes(igame_,n);
		  }	  
			


		///Lights
		  for (int i = 0; i < light.size(); ++i) 
		  {
			IGameNode * n = light[i];
			GMatrix otm = n ->GetWorldTM(coreFrame_);			
			IGameObject *obj =  n->GetIGameObject();
			IGameLight * gc = static_cast<IGameLight *>(obj);  
			
			AdjustMatrix(otm);		
			AffineParts apVal;
			decomp_affine(otm.ExtractMatrix3(), &apVal);

			Point4 quat;
			quat.x = apVal.q.x;
			quat.y = apVal.q.y;
			quat.z = apVal.q.z;
			quat.w = apVal.q.w;
			//writer->WriteTransformation( apVal.t,quat,apVal.k);						
		  }
		  
		  ///Cameras
		  if(camera.size() >0)
		  {
			  writer->BeginWriteCameras();
		  }

		  for (int i = 0; i < camera.size(); ++i) 
		  {
			IGameNode * n = camera[i];
			GMatrix otm = n ->GetWorldTM(coreFrame_);			
			IGameObject *obj =  n->GetIGameObject();
			IGameCamera * gc = static_cast<IGameCamera *>(obj);  						
			float nc;
			float fc;
			float fov;
			gc->GetCameraNearClip()->GetPropertyValue(nc,coreFrame_);
			gc->GetCameraFarClip()->GetPropertyValue(fc,coreFrame_);			
			gc->GetCameraFOV()->GetPropertyValue(fov,coreFrame_);
			
			IGameNode *target = gc->GetCameraTarget();
			GMatrix targetM = target ->GetWorldTM(coreFrame_);	
			
			AdjustMatrix(otm);		
			AffineParts apVal;
			decomp_affine(otm.ExtractMatrix3(), &apVal);
			
			AdjustMatrix(targetM);		
			AffineParts apVal2;
			decomp_affine(targetM.ExtractMatrix3(), &apVal2);

			writer->WriteCamera(n->GetName(),nc,fc,fov,apVal.t,apVal2.t);
		  }

		  if(camera.size() >0)
		  {
			  writer->EndWriteCameras();
		  }


	     if(helper.size()>0)
			writer->BeginWriteHelpers();
		  ///HELPERS
		for (int i = 0; i < helper.size(); ++i) 
		{			
			IGameNode * n = helper[i];
			GMatrix otm = n ->GetWorldTM(coreFrame_);			
			IGameObject *obj =  n->GetIGameObject();
			IGameSupportObject * gc = static_cast<IGameSupportObject *>(obj);  						

			AdjustMatrix(otm);		
			AffineParts apVal;
			decomp_affine(otm.ExtractMatrix3(), &apVal);

			Point4 quat;
			quat.x = apVal.q.x;
			quat.y = apVal.q.y;
			quat.z = apVal.q.z;
			quat.w = apVal.q.w;
			writer->WriteHelper(n->GetName(), apVal.t,quat,apVal.k);						
		}
		if(helper.size()>0)
			writer->EndWriteHelpers();


		writer->BeginWriteModels();
		  
		  std::vector<IGameNode *> animated;
		  for (int i = 0; i < models.size(); ++i) 
		  {
			IGameNode * n = models[i];
			GMatrix otm = n ->GetWorldTM(coreFrame_);
			//GMatrix otm = n->GetObjectTM(coreFrame_);
			AdjustMatrix(otm);
					
			IGameObject *obj =  n->GetIGameObject();
			writer->BeginWriteModel(n->GetName());

			AffineParts apVal;
			decomp_affine(otm.ExtractMatrix3(), &apVal);

			Point4 quat;
			quat.x = apVal.q.x;
			quat.y = apVal.q.y;
			quat.z = apVal.q.z;
			quat.w = apVal.q.w;
			writer->WriteTransformation( apVal.t,quat,apVal.k);			

			WriteGeometry(igame_, n, obj,coreFrame_);

			writer->EndWriteModel();

			bones.clear();
			
		  }
			
		  writer->EndWriteModels();

		  

		  writer->EndFile();

	if(!suppressPrompts)
		DialogBoxParam(hInstance, 
				MAKEINTRESOURCE(IDD_PANEL), 
				GetActiveWindow(), 
				Project1OptionsDlgProc, (LPARAM)this);

	#pragma message(TODO("return TRUE If the file is exported properly"))
	return FALSE;
}


